/**
 * @file graphics.c
 * @author MD, md@tomatesasesinos.com
 * @date 2007
 * 
 * Program: Solo, a computer solitarie card game.
 * By: MD, md@tomatesasesinos.com
 * Copyright 2007
 *
 * License: GPL http://www.gnu.org/licences/gpl.html
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * Contents: Graphics functions
 */

#include "globals.h"

/**
 * The main function to show the solitarie and interactue with player.
 */
void solitaireSDL(void)
{
	SDL_Event event;
	SDLKey key;

	startSDL();
	loadImages();
	startWindow();
	startCursors();

	showBoard();
	paintCursors();
	
	SDL_Flip(screen);

	while (TRUE)
	{
		if (SDL_WaitEvent(&event))
		{
			SDL_PollEvent(&event);
			switch (event.type)
			{
				case SDL_KEYDOWN:
					key = event.key.keysym.sym;
					switch (key)
					{
						case SDLK_UP:
							moveBoxPointerKeyboard(UP);
							break;
						case SDLK_DOWN:
							moveBoxPointerKeyboard(DOWN);
							break;
						case SDLK_RIGHT:
							moveBoxPointerKeyboard(RIGHT);
							break;
						case SDLK_LEFT:
							moveBoxPointerKeyboard(LEFT);
							break;
						case SDLK_SPACE:
							action();
							break;
						case SDLK_ESCAPE:
							exit(0); //TODO: This in future will go to small menu.
							break;
					}
					break;
				case SDL_QUIT:
					exit(0);
					break;
				case SDL_VIDEOEXPOSE:
					//for improved
					showBoard();
					paintCursors();
					SDL_Flip(screen);
					break;
			}
		}
		if (win())
		{
			//Show the Win message, and wait press any key to exit.
			while (1)
			{
				showBoard();
				SDL_BlitSurface(winTextSurface, NULL, screen, NULL);
				SDL_Flip(screen);
				if (SDL_WaitEvent(&event))
				{
					SDL_PollEvent(&event);
					if (event.type == SDL_KEYDOWN)
					{
						key=event.key.keysym.sym;
						if(key == SDLK_ESCAPE) exit(0);
					}
					if(event.type == SDL_QUIT) exit(0);
				}
			}
		}
	}

}

void startCursors(void)
{
	int iterator;
	
	defaultCursorSurface = IMG_Load(FILE_IMG_SEL_BLUE);
	defaultCursorSurface_bottomcup = IMG_Load(FILE_IMG_SEL_BOTTOMCUP_BLUE);
	defaultCursorSurface_topcup = IMG_Load(FILE_IMG_SEL_TOPCUP_BLUE);
	defaultCursorSurface_piece = IMG_Load(FILE_IMG_SEL_PIECE_BLUE);
	
	originActionCursorSurface = IMG_Load(FILE_IMG_SEL_RED);
	originActionCursorSurface_bottomcup = IMG_Load(FILE_IMG_SEL_BOTTOMCUP_RED);
	originActionCursorSurface_topcup = IMG_Load(FILE_IMG_SEL_TOPCUP_RED);
	originActionCursorSurface_piece = IMG_Load(FILE_IMG_SEL_PIECE_RED);
	
	actionInPogress = FALSE;

	//Clear or disabled all zones or places for cursor
	for (iterator = 0; iterator < NUM_BOARD_PLACES; iterator++)
		possibleZonesCursorInBoard[iterator] = FALSE;
	
	possibleZonesCursorInBoard[PILE_DRAW_OUT] = TRUE; //And enable or set in pile of draw out.
	
	for (iterator = COLUMN1; iterator< END_COLUMNS; iterator++)
		possibleZonesCursorInBoard[iterator] = TRUE; //And enable or set the columns

	posCursor = POSITION_CURSOR(0, PILE_DRAW_OUT);
}

void loadImages(void)
{
	cardsSurface = IMG_Load(FILE_IMG_CARDS);
	backFaceCardSurface = IMG_Load(FILE_IMG_BACKFACE_CARD);
	emptyTextSurface = IMG_Load(FILE_IMG_EMPTY_TEXT);
	winTextSurface = IMG_Load(FILE_IMG_WIN_TEXT);
}

void startWindow(void)
{
	SDL_WM_SetCaption("Solo",NULL);
	screen = SDL_SetVideoMode(SCREEN_W, SCREEN_H, cardsSurface->format->BitsPerPixel, SDL_ANYFORMAT);
}

void startSDL(void)
{
	if (SDL_Init(SDL_INIT_VIDEO) < 0)
	{
		fprintf(stderr,"ERROR: Can not start SDL: %s\n",SDL_GetError());
		exit(1);
	}
	atexit(SDL_Quit);
}

void action(void)
{
	int positionInZoneBoard = POSITION_CURSOR_TO_POS_IN_ZONE_BOARD(posCursor);
	int posInitActionCursorInZoneBoard = POSITION_CURSOR_TO_POS_IN_ZONE_BOARD(posInitActionCursor);
	int positionStairFaceUp = POSITION_CURSOR_TO_POS_IN_STAIR_FACE_UP(posCursor);
	int posInitActionCursorStairFaceUp = POSITION_CURSOR_TO_POS_IN_STAIR_FACE_UP(posInitActionCursor);

	switch (positionInZoneBoard)
	{
		case PILE_DRAW_OUT:
			actionInPogress = FALSE;
			actionExtract();
			break;
		default:
			if (actionInPogress) //if select one or more card in previus move.
			{
				actionInPogress = FALSE;
				if (posInitActionCursorStairFaceUp == 0) // if it's first card in the bottom pile stair.
				{
					if ((positionInZoneBoard != PILE_CARDS_DRAW) &&
						(positionInZoneBoard != posInitActionCursorInZoneBoard))
					{
						legalMoveCard(posInitActionCursorInZoneBoard, positionInZoneBoard);
						showBoard();
						paintCursors();
						SDL_Flip(screen);
					}
					else
					{
						//For the double click that in this game move automaticaly card to the suit pile.
						if (positionInZoneBoard == posInitActionCursorInZoneBoard)
						{
							if (moveCardToPileSuit(positionInZoneBoard))
							{
								showBoard();
								paintCursors();
								SDL_Flip(screen);
							}
							else
							{
								//TODO: make a noise :)
								showBoard();
								paintCursors();
								SDL_Flip(screen);
							}
						}
					}
				}
				else
				{
					legalMoveGroupCards( posInitActionCursorInZoneBoard,
						posInitActionCursorStairFaceUp, positionInZoneBoard);
					showBoard();
					paintCursors();
					SDL_Flip(screen);
				}

			}
			else // Not select one or more card, there is not action in progress.
			{
				if (board[positionInZoneBoard][0].numCard != NO_CARD)
				{
					if (positionStairFaceUp == 0) ////the action is on one card
					{
						if(board[positionInZoneBoard][0].faceCard == FACE_UP) //Mark with previusCursors this card.
						{
							actionInPogress = TRUE;
							posInitActionCursor = posCursor;
							
							showBoard();
							paintCursors();
							SDL_Flip(screen);
						}
						else //If the card is face down, flip to face up the card.
						{
							board[positionInZoneBoard][0].faceCard = FACE_UP;
							numCardsFaceUpInZone[positionInZoneBoard - COLUMN1]++;
							showBoard();
							paintCursors();
							SDL_Flip(screen);
						}
					}
					else //The action is on a group cards.
					{
						actionInPogress = TRUE;
						posInitActionCursor = posCursor;
						
						showBoard();
						paintCursors();
						SDL_Flip(screen);
					}
				}
			}
			break;
	}
}

void actionExtract(void)
{
	if (numCardsBoardZones[PILE_DRAW_OUT] > 0)
	{
		extractCardPileDrawOut();
		showBoard();
		paintCursors();
		SDL_Flip(screen);
	}else
	{
		refillPileDrawOut();
		showBoard();
		paintCursors();
		SDL_Flip(screen);
	}
}

void moveBoxPointerKeyboard(int direction)
{
	int positionInZoneBoard = POSITION_CURSOR_TO_POS_IN_ZONE_BOARD(posCursor);
	int positionStairFaceUp = POSITION_CURSOR_TO_POS_IN_STAIR_FACE_UP(posCursor);

	switch (direction)
	{
		case UP:
			if (positionInZoneBoard >= COLUMN1)
			{
				if (numCardsFaceUpInZone[positionInZoneBoard-COLUMN1] > 1)
				{
					if (numCardsFaceUpInZone[positionInZoneBoard - COLUMN1] > (positionStairFaceUp + 1))
						positionStairFaceUp++; //Up to next card
					else
					{
						//Jump to Piles cards in the first row of table.
						
						positionStairFaceUp = 0;
						if(positionInZoneBoard != COLUMN3)
						{
							if(positionInZoneBoard <= COLUMN2) positionInZoneBoard = positionInZoneBoard - 6;
							else positionInZoneBoard = positionInZoneBoard - 7;
						}
					}
				}
				else
				{
					if (positionInZoneBoard != COLUMN3)
					{
						positionStairFaceUp = 0;
						if (positionInZoneBoard <= COLUMN2) positionInZoneBoard = positionInZoneBoard - 6;
						else positionInZoneBoard = positionInZoneBoard - 7;
					}
				}
			}
			else
			{
				if (positionInZoneBoard < COLUMN1)
				{
					positionStairFaceUp = 0;
					if (positionInZoneBoard <= PILE_CARDS_DRAW) positionInZoneBoard = positionInZoneBoard + 6;
					else positionInZoneBoard = positionInZoneBoard + 7;
				}
			}
			break;
		case DOWN:
			if (positionInZoneBoard < COLUMN1)
			{
				positionStairFaceUp = 0;
				if (positionInZoneBoard <= PILE_CARDS_DRAW) positionInZoneBoard = positionInZoneBoard + 6;
				else positionInZoneBoard = positionInZoneBoard + 7;
			}
			else
			{
				if (positionStairFaceUp > 0) positionStairFaceUp--;
				else
				{
					if(positionInZoneBoard != COLUMN3)
					{
						positionStairFaceUp=0;
						if (positionInZoneBoard <= COLUMN2) positionInZoneBoard = positionInZoneBoard - 6;
						else positionInZoneBoard = positionInZoneBoard - 7;
					}
				}
			}
			break;
		case LEFT:
			if ((positionInZoneBoard != PILE_DRAW_OUT) && (positionInZoneBoard != COLUMN1))
			{
				positionInZoneBoard--;
			}
			else
			{
				if (positionInZoneBoard == COLUMN1) positionInZoneBoard = COLUMN7;
				if (positionInZoneBoard == PILE_DRAW_OUT) positionInZoneBoard = PILE_SUIT4;
			}
			positionStairFaceUp = 0;
			break;
		case RIGHT:
			if ((positionInZoneBoard != PILE_SUIT4) && (positionInZoneBoard != COLUMN7))
			{
				positionInZoneBoard++;
			}
			else
			{
				if (positionInZoneBoard == COLUMN7) positionInZoneBoard = COLUMN1;
				if (positionInZoneBoard == PILE_SUIT4) positionInZoneBoard = PILE_DRAW_OUT;
			}
			positionStairFaceUp = 0;
		break;
	}

	posCursor = POSITION_CURSOR(positionStairFaceUp,positionInZoneBoard);

	showBoard();
	paintCursors();
	SDL_Flip(screen);
}

/*SDL_Surface *crearSuperficie(int w, int h)
{
	SDL_Surface *retorno=NULL;

	retorno=SDL_CreateRGBSurface(SDL_SRCALPHA,w,h,screen->format->BitsPerPixel,screen->format->Rmask,screen->format->Gmask,screen->format->Bmask,screen->format->Amask);

	return retorno;
}*/

void paintCursor(int cursor)
{
	int positionInZoneBoard;
	int positionInStairFaceUp;
	int iterator;
	SDL_Surface *cursorSurface = NULL;
	SDL_Surface *cursorSurface_bottomcup = NULL;
	SDL_Surface *cursorSurface_piece = NULL;
	SDL_Surface *cursorSurface_topcup = NULL;
	
	SDL_Rect cordsCursor;
	
	switch (cursor)
	{
		case CURSOR_MOVE:
			positionInZoneBoard = POSITION_CURSOR_TO_POS_IN_ZONE_BOARD(posCursor);
			positionInStairFaceUp = POSITION_CURSOR_TO_POS_IN_STAIR_FACE_UP(posCursor);
			
			cursorSurface = defaultCursorSurface;
			cursorSurface_bottomcup = defaultCursorSurface_bottomcup;
			cursorSurface_piece = defaultCursorSurface_piece;
			cursorSurface_topcup = defaultCursorSurface_topcup;
			break;
		case CURSOR_ACTION:
			positionInZoneBoard = POSITION_CURSOR_TO_POS_IN_ZONE_BOARD(posInitActionCursor);
			positionInStairFaceUp = POSITION_CURSOR_TO_POS_IN_STAIR_FACE_UP(posInitActionCursor);
			
			cursorSurface = originActionCursorSurface;
			cursorSurface_bottomcup = originActionCursorSurface_bottomcup;
			cursorSurface_piece = originActionCursorSurface_piece;
			cursorSurface_topcup = originActionCursorSurface_topcup;
			break;
	}
	
	//Axis Y
	if (positionInZoneBoard >= COLUMN1)
	{
		cordsCursor.y = CARD_HEIGHT + MARGIN_BETWEEN_CARDS + SCREEN_BORDER;
		
		if (numCardsBoardZones[positionInZoneBoard] > 0)
		{
			if (numCardsFaceUpInZone[positionInZoneBoard - COLUMN1] > 0)
			{
				cordsCursor.y = cordsCursor.y +
					(MARGIN_BETWEEN_CARD_FACE_DOWN_CARD *
						(numCardsBoardZones[positionInZoneBoard] - numCardsFaceUpInZone[positionInZoneBoard - COLUMN1])) +
						(VERTICAL_MARGIN_BETWEEN_CARDS * (numCardsFaceUpInZone[positionInZoneBoard - COLUMN1] - 1));
			}
			else
				cordsCursor.y = cordsCursor.y +
					(MARGIN_BETWEEN_CARD_FACE_DOWN_CARD * (numCardsBoardZones[positionInZoneBoard] - 1));
		}
	}
	else cordsCursor.y = SCREEN_BORDER;
	
	//Axis x
	if(positionInZoneBoard >= COLUMN1)
		cordsCursor.x = SCREEN_BORDER + (CARD_WIDTH + MARGIN_BETWEEN_CARDS) * (positionInZoneBoard - COLUMN1);
	else
	{
		if (positionInZoneBoard >= PILE_SUIT1)
			cordsCursor.x = SCREEN_BORDER + (CARD_WIDTH + MARGIN_BETWEEN_CARDS) * (positionInZoneBoard + 1);
		else cordsCursor.x = SCREEN_BORDER + (CARD_WIDTH + MARGIN_BETWEEN_CARDS) * positionInZoneBoard;
	}
	
	if (positionInStairFaceUp == 0) {
		//Paint the cursor only one card.
		SDL_BlitSurface( cursorSurface, NULL, screen, &cordsCursor);
	}
	else {
		//Paint the cursor group several cards.
		SDL_BlitSurface( cursorSurface_bottomcup, NULL, screen, &cordsCursor);
		if (positionInStairFaceUp > 1) {
			for (iterator = 1; iterator < positionInStairFaceUp; iterator++) {
				cordsCursor.y = cordsCursor.y - VERTICAL_MARGIN_BETWEEN_CARDS;
				SDL_BlitSurface( cursorSurface_piece, NULL, screen, &cordsCursor);
			}
		}
		cordsCursor.y = cordsCursor.y -
			VERTICAL_MARGIN_BETWEEN_CARDS;
		SDL_BlitSurface( cursorSurface_topcup, NULL, screen, &cordsCursor);
	}
}

void paintCursors(void)
{
	if (actionInPogress) {
		paintCursor(CURSOR_ACTION);
	}
	paintCursor(CURSOR_MOVE);
}

void blitCard(int zoneBoard, int posInStairPile, int x, int y)
{
	SDL_Surface *image = NULL;
	SDL_Rect *cellSprite = NULL;
	SDL_Rect position;

	if (board[zoneBoard][posInStairPile].numCard != NO_CARD)
	{
		if (board[zoneBoard][posInStairPile].faceCard == FACE_UP)
		{
			cellSprite = numCartToSprite(board[zoneBoard][posInStairPile].numCard);
			image = cardsSurface;
		}
		else
		{
			cellSprite = NULL;
			image = backFaceCardSurface;
		}
		position.x = x + SCREEN_BORDER;
		position.y = y + SCREEN_BORDER;

		SDL_BlitSurface(image, cellSprite, screen, &position);
	}
}

void paintStairPileCardUp(void)
{
	int i,j;
	int x,y;

	x = 0;

	for (i = COLUMN1; i < END_COLUMNS; i++)
	{
		y = CARD_HEIGHT + MARGIN_BETWEEN_CARDS;

		//loop back to front or end pile to start pile
		for (j = numCardsBoardZones[i] - 1; j >= 0; j--)
		{
			blitCard(i,j,x,y);
			if (board[i][j].faceCard == FACE_UP) y = y + VERTICAL_MARGIN_BETWEEN_CARDS;
			else y = y + MARGIN_BETWEEN_CARD_FACE_DOWN_CARD;
		}
		x = x + CARD_WIDTH + MARGIN_BETWEEN_CARDS;
	}
}

void paintPiles(void)
{
	int iterator;
	SDL_Rect positionEmptyTextSurface;
	int x = 0;
	int y = 0;

	positionEmptyTextSurface.x = SCREEN_BORDER;
	positionEmptyTextSurface.y = positionEmptyTextSurface.x;

	for(iterator = 0; iterator < END_PILES; iterator++)
	{

		//This secuence of "if" is for do ilusional optical the tall pile of cards and see false lade of pile as grow.
		if (numCardsBoardZones[iterator] > 12)
			blitFirstCard( iterator, x - (MARGIN_BETWEEN_CARDS_IN_PILE * 3), y - (MARGIN_BETWEEN_CARDS_IN_PILE*3));
		if (numCardsBoardZones[iterator] > 8)
			blitFirstCard( iterator, x - (MARGIN_BETWEEN_CARDS_IN_PILE * 2), y - (MARGIN_BETWEEN_CARDS_IN_PILE * 2));
		if (numCardsBoardZones[iterator] > 4)
			blitFirstCard( iterator, x - MARGIN_BETWEEN_CARDS_IN_PILE, y - MARGIN_BETWEEN_CARDS_IN_PILE);

		blitFirstCard(iterator,x,y);

		x = x + (CARD_WIDTH + MARGIN_BETWEEN_CARDS);
		if (iterator == PILE_CARDS_DRAW) x = x + (CARD_WIDTH + MARGIN_BETWEEN_CARDS); //Space between pile cards draw and first pile suit.
	}

	if (numCardsBoardZones[PILE_DRAW_OUT] == 0)
		SDL_BlitSurface(emptyTextSurface, NULL, screen, &positionEmptyTextSurface);
}

void paintBorderRectangleOfPilesSuit(void)
{
	SDL_Rect innerRectangleBorder, outerRectangleBorder;
	int iterator;

	innerRectangleBorder.w = CARD_WIDTH + MARGIN_BETWEEN_CARDS_AND_BORDER_PILES_SUIT * 2;
	innerRectangleBorder.h = CARD_HEIGHT + MARGIN_BETWEEN_CARDS_AND_BORDER_PILES_SUIT * 2;
	outerRectangleBorder.w = innerRectangleBorder.w + WIDTH_BORDER_PILES_SUIT * 2;
	outerRectangleBorder.h = innerRectangleBorder.h + WIDTH_BORDER_PILES_SUIT * 2;

	outerRectangleBorder.x = ( SCREEN_BORDER + (CARD_WIDTH * 3 + MARGIN_BETWEEN_CARDS * 3)) -
		(MARGIN_BETWEEN_CARDS_AND_BORDER_PILES_SUIT + WIDTH_BORDER_PILES_SUIT);
	outerRectangleBorder.y = SCREEN_BORDER - (WIDTH_BORDER_PILES_SUIT + MARGIN_BETWEEN_CARDS_AND_BORDER_PILES_SUIT);

	innerRectangleBorder.x = outerRectangleBorder.x + WIDTH_BORDER_PILES_SUIT;
	innerRectangleBorder.y = outerRectangleBorder.y + WIDTH_BORDER_PILES_SUIT;

	for(iterator = 0; iterator < NUM_SUIT_IN_DECK; iterator++)
	{
		SDL_FillRect(screen, &outerRectangleBorder, SDL_MapRGB(screen->format, COLOR_BORDER_RECTANGLE_PILES_SUIT));
		SDL_FillRect(screen, &innerRectangleBorder, SDL_MapRGB(screen->format, COLOR_BACKGROUND_BOARD));

		outerRectangleBorder.x = outerRectangleBorder.x + MARGIN_BETWEEN_CARDS + CARD_WIDTH;
		innerRectangleBorder.x = innerRectangleBorder.x + MARGIN_BETWEEN_CARDS + CARD_WIDTH;
	}
}

void showBoard(void)
{
	//At the moment (for fix or update) the procedure repaint all screen.
	SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format,COLOR_BACKGROUND_BOARD));
	paintBorderRectangleOfPilesSuit();
	paintPiles();
	paintStairPileCardUp();
}

void blitFirstCard(int zoneBoard, int x,int y)
{
	blitCard(zoneBoard,0,x,y);
}

SDL_Rect *numCartToSprite(int numCard)
{
	SDL_Rect *returnValue = NULL;

	returnValue = (SDL_Rect *)malloc(sizeof(SDL_Rect));
	returnValue->w = CARD_WIDTH;
	returnValue->h = CARD_HEIGHT;

	returnValue->x = (numCard % NUM_CARDS_IN_SUIT) * CARD_WIDTH;
	returnValue->y = (numCard / NUM_CARDS_IN_SUIT) * CARD_HEIGHT;

	return returnValue;
}
